Skip to content

Use of Windows certificate store for authentication#900

Open
JacobBarthelmeh wants to merge 11 commits intowolfSSL:masterfrom
JacobBarthelmeh:winSysCerts
Open

Use of Windows certificate store for authentication#900
JacobBarthelmeh wants to merge 11 commits intowolfSSL:masterfrom
JacobBarthelmeh:winSysCerts

Conversation

@JacobBarthelmeh
Copy link
Copy Markdown
Contributor

No description provided.

@JacobBarthelmeh JacobBarthelmeh self-assigned this Mar 25, 2026
Copilot AI review requested due to automatic review settings March 25, 2026 12:38
fix for memory leak and windows build issues

sanity check on input and refactor parse cert store spec function
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR adds Windows Certificate Store integration to wolfSSH so host keys and client authentication keys can be sourced from the Windows cert store (including CI coverage on Windows).

Changes:

  • Add a Windows-only API to load a private key by locating a certificate in the Windows Certificate Store, and use CNG to sign during SSH handshakes/auth.
  • Extend cert manager plumbing and wolfsshd configuration to support system/user CA loading and cert-store-based host keys.
  • Update Windows build projects and add a GitHub Actions workflow to exercise file-vs-store interop permutations.

Reviewed changes

Copilot reviewed 22 out of 22 changed files in this pull request and generated 8 comments.

Show a summary per file
File Description
wolfssh/test.h Prefer wolfCrypt Base16 when available; otherwise keep local Base16 decode helper.
wolfssh/ssh.h Add wolfSSH_CTX_UsePrivateKey_fromStore() Windows-only public API.
wolfssh/internal.h Add CTX private-key metadata for cert-store backed keys and internal helper prototypes.
wolfssh/certman.h Expose cert-manager setter and Windows cert-store spec parser API.
src/ssh.c Implement loading a CTX private key from the Windows Certificate Store.
src/internal.c Add cert-store signing path (CNG) and cert-derived RSA public-key extraction for KEX/auth flows.
src/certman.c Implement wolfSSH_SetCertManager() and wolfSSH_ParseCertStoreSpec().
ide/winvs/wolfsshd/wolfsshd.vcxproj Link against crypt32/ncrypt for cert-store features.
ide/winvs/wolfssh/wolfssh.vcxproj Link against crypt32/ncrypt for cert-store features.
ide/winvs/wolfsftp-client/wolfsftp-client.vcxproj Link against crypt32/ncrypt for cert-store features.
ide/winvs/unit-test/unit-test.vcxproj Link against crypt32/ncrypt for cert-store features; normalize XML header.
ide/winvs/echoserver/echoserver.vcxproj Link against crypt32/ncrypt for cert-store features.
ide/winvs/client/client.vcxproj Link against crypt32/ncrypt for cert-store features.
ide/winvs/api-test/api-test.vcxproj Link against crypt32/ncrypt for cert-store features; normalize XML header.
examples/sftpclient/sftpclient.c Add -W store:subject:flags support for client key from Windows cert store.
examples/echoserver/echoserver.c Add -W support for server host key from Windows cert store; skip key-file root search when using store.
examples/client/common.h Declare helper functions for cert-store key loading/auth setup.
examples/client/common.c Implement cert-store key loading wrapper + auth globals setup for x509v3 publickey auth.
apps/wolfsshd/wolfsshd.c Add host-key-from-store support and optional system/user CA store loading into wolfSSH cert manager.
apps/wolfsshd/configuration.h Add config getters for host-key store and Windows user-CA store options.
apps/wolfsshd/configuration.c Add parsing/storage for new config directives and defaults.
.github/workflows/windows-cert-store-test.yml Add Windows CI workflow to validate store/file combinations.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Copilot AI review requested due to automatic review settings April 14, 2026 03:24
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 21 out of 22 changed files in this pull request and generated 5 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +44 to +46
WOLFSSH_API
int wolfSSH_SetCertManager(WOLFSSH_CTX* ctx, WOLFSSL_CERT_MANAGER* cm);

Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

wolfSSH_SetCertManager() uses WOLFSSH_CTX* but this header does not declare/forward-declare WOLFSSH_CTX (and it doesn’t include <wolfssh/ssh.h>). Any translation unit including wolfssh/certman.h without including wolfssh/ssh.h first will fail to compile. Add a forward declaration (typedef struct WOLFSSH_CTX WOLFSSH_CTX;) or include the appropriate header before using the type here.

Copilot uses AI. Check for mistakes.
Comment on lines +2641 to +2650
if (ctx->privateKey[keyIdx].certStoreContext != NULL)
CertFreeCertificateContext(
(PCCERT_CONTEXT)ctx->privateKey[keyIdx].certStoreContext);
if (ctx->privateKey[keyIdx].storeName != NULL)
WFREE(ctx->privateKey[keyIdx].storeName, heap, DYNTYPE_STRING);
if (ctx->privateKey[keyIdx].subjectName != NULL)
WFREE(ctx->privateKey[keyIdx].subjectName, heap, DYNTYPE_STRING);
if (ctx->privateKey[keyIdx].cert != NULL)
WFREE(ctx->privateKey[keyIdx].cert, heap, DYNTYPE_CERT);
}
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

When reusing an existing private-key slot (same publicKeyFmt), this cleanup only runs if the existing slot already had useCertStore=1. If the slot previously held a file-based key/cert, its key (and possibly cert) buffers are never freed/zeroed before being overwritten, causing leaks and leaving stale key material attached to the slot. Consider mirroring SetHostPrivateKey() cleanup: free/ForceZero any existing key/keySz, free any existing cert/certSz, and clear related pointers regardless of useCertStore before assigning the cert-store-backed key.

Suggested change
if (ctx->privateKey[keyIdx].certStoreContext != NULL)
CertFreeCertificateContext(
(PCCERT_CONTEXT)ctx->privateKey[keyIdx].certStoreContext);
if (ctx->privateKey[keyIdx].storeName != NULL)
WFREE(ctx->privateKey[keyIdx].storeName, heap, DYNTYPE_STRING);
if (ctx->privateKey[keyIdx].subjectName != NULL)
WFREE(ctx->privateKey[keyIdx].subjectName, heap, DYNTYPE_STRING);
if (ctx->privateKey[keyIdx].cert != NULL)
WFREE(ctx->privateKey[keyIdx].cert, heap, DYNTYPE_CERT);
}
if (ctx->privateKey[keyIdx].certStoreContext != NULL) {
CertFreeCertificateContext(
(PCCERT_CONTEXT)ctx->privateKey[keyIdx].certStoreContext);
ctx->privateKey[keyIdx].certStoreContext = NULL;
}
if (ctx->privateKey[keyIdx].storeName != NULL) {
WFREE(ctx->privateKey[keyIdx].storeName, heap, DYNTYPE_STRING);
ctx->privateKey[keyIdx].storeName = NULL;
}
if (ctx->privateKey[keyIdx].subjectName != NULL) {
WFREE(ctx->privateKey[keyIdx].subjectName, heap, DYNTYPE_STRING);
ctx->privateKey[keyIdx].subjectName = NULL;
}
}
if (ctx->privateKey[keyIdx].key != NULL) {
ForceZero(ctx->privateKey[keyIdx].key, ctx->privateKey[keyIdx].keySz);
WFREE(ctx->privateKey[keyIdx].key, heap, DYNTYPE_PRIVKEY);
ctx->privateKey[keyIdx].key = NULL;
ctx->privateKey[keyIdx].keySz = 0;
}
if (ctx->privateKey[keyIdx].cert != NULL) {
WFREE(ctx->privateKey[keyIdx].cert, heap, DYNTYPE_CERT);
ctx->privateKey[keyIdx].cert = NULL;
ctx->privateKey[keyIdx].certSz = 0;
}

Copilot uses AI. Check for mistakes.
Comment on lines +2547 to +2553
if (pCertContext == NULL) {
/* Try finding by thumbprint if subject name didn't work */
/* Note: subjectName could be a thumbprint in format "XX XX XX ..." */
CertCloseStore(hStore, 0);
WLOG(WS_LOG_ERROR, "wolfSSH_CTX_UsePrivateKey_fromStore: Certificate "
"not found with subject '%ls'", subjectName);
return WS_FATAL_ERROR;
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The code comments say it will "Try finding by thumbprint" when subject lookup fails, but the implementation returns immediately without attempting any thumbprint-based search (e.g., CERT_FIND_HASH). This is misleading for callers and future maintenance; either implement the thumbprint lookup or remove/adjust the comment to match actual behavior.

Copilot uses AI. Check for mistakes.
Comment on lines +388 to +409
/* Convert to wide strings */
storeNameLen = MultiByteToWideChar(CP_UTF8, 0, hostKeyStore, -1, NULL, 0);
subjectNameLen = MultiByteToWideChar(CP_UTF8, 0, hostKeyStoreSubject, -1, NULL, 0);

wStoreName = (wchar_t*)WMALLOC(storeNameLen * sizeof(wchar_t), heap, DYNTYPE_SSHD);
wSubjectName = (wchar_t*)WMALLOC(subjectNameLen * sizeof(wchar_t), heap, DYNTYPE_SSHD);

if (wStoreName == NULL || wSubjectName == NULL) {
wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Memory allocation failed for cert store strings");
ret = WS_MEMORY_E;
} else {
MultiByteToWideChar(CP_UTF8, 0, hostKeyStore, -1, wStoreName, storeNameLen);
MultiByteToWideChar(CP_UTF8, 0, hostKeyStoreSubject, -1, wSubjectName, subjectNameLen);

ret = wolfSSH_CTX_UsePrivateKey_fromStore(*ctx, wStoreName, dwFlags, wSubjectName);
if (ret != WS_SUCCESS) {
wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Failed to load host key from certificate store");
}

WFREE(wStoreName, heap, DYNTYPE_SSHD);
WFREE(wSubjectName, heap, DYNTYPE_SSHD);
}
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MultiByteToWideChar() return values are not checked before being used as allocation sizes. If conversion fails, it can return 0, leading to zero-sized allocations and subsequent calls that will fail (or worse, write into undersized buffers). Also, if one of WMALLOC allocations succeeds and the other fails, the successful allocation is leaked. Please validate storeNameLen/subjectNameLen > 0, handle GetLastError()/conversion failures, and free any partially allocated wide strings on error paths.

Copilot uses AI. Check for mistakes.
ret = WS_BAD_ARGUMENT;
}

if (ret == WS_SUCCESS) {
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These setters assign a newly allocated string via CreateString() without freeing any previously set value, which leaks memory if the config option is specified more than once (or if config structs are reused). Consider freeing conf->winUserStores first (e.g., FreeString) or using the existing SetFileString() helper which handles replacement safely.

Suggested change
if (ret == WS_SUCCESS) {
if (ret == WS_SUCCESS) {
if (conf->winUserStores != NULL) {
WFREE(conf->winUserStores, conf->heap, DYNTYPE_STRING);
conf->winUserStores = NULL;
}

Copilot uses AI. Check for mistakes.
Copilot AI review requested due to automatic review settings April 14, 2026 16:30
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 22 out of 23 changed files in this pull request and generated 9 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines +402 to +406
#ifdef WOLFSSH_WINDOWS_CERT_STORE
WOLFSSH_API int wolfSSH_CTX_UsePrivateKey_fromStore(WOLFSSH_CTX* ctx,
const wchar_t* storeName, DWORD dwFlags,
const wchar_t* subjectName);
#endif /* WOLFSSH_WINDOWS_CERT_STORE */
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This public header introduces wchar_t and DWORD in the API but does not include headers that define DWORD (and may not reliably provide wchar_t in C builds). This will fail to compile for consumers that enable WOLFSSH_WINDOWS_CERT_STORE without having included <windows.h> first. Consider adding a guarded include (e.g., under #if defined(_WIN32) && defined(WOLFSSH_WINDOWS_CERT_STORE)) for <windows.h> (or <windef.h>), and include <wchar.h>/<stddef.h> for wchar_t, or change the API to use wolfSSH-owned typedefs (e.g., word32 for flags) to avoid Windows types in public headers.

Copilot uses AI. Check for mistakes.
/* Certificate store name (e.g., "My", "Root"). Owned by CTX. */
wchar_t* subjectName;
/* Certificate subject name or thumbprint for lookup. Owned by CTX. */
DWORD dwFlags;
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

DWORD appears in an internal header without any guarantee that Windows headers are included prior to this header. If WOLFSSH_WINDOWS_CERT_STORE is defined and this header is included in a non-Windows translation unit (or before <windows.h>), builds will break. Consider either including <windows.h> inside this #ifdef (and guarding it with _WIN32) or replacing DWORD with a project type (e.g., word32) inside the struct to avoid depending on Windows typedefs in headers.

Suggested change
DWORD dwFlags;
word32 dwFlags;

Copilot uses AI. Check for mistakes.
Comment on lines +2535 to +2537
if (pCertContext == NULL && wcslen(subjectName) > 3 &&
(wcsncmp(subjectName, L"CN=", 3) == 0 ||
wcsncmp(subjectName, L"cn=", 3) == 0)) {
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This code uses wcslen/wcsncmp but the file’s Windows include block adds <string.h> (narrow-string) and does not include <wchar.h>, which is the correct header for wide-string functions. This can cause implicit declaration warnings/errors depending on the compiler settings. Add #include <wchar.h> under #ifdef WOLFSSH_WINDOWS_CERT_STORE.

Copilot uses AI. Check for mistakes.
Comment on lines +12338 to +12344
/* Get the private key handle from the certificate */
if (!CryptAcquireCertificatePrivateKey(pCertContext,
CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG | CRYPT_ACQUIRE_SILENT_FLAG,
NULL, &hCryptProv, &dwKeySpec, &fCallerFreeProv)) {
DWORD dwErr = GetLastError();
WLOG(WS_LOG_DEBUG, "SignWithCertStoreKey: Failed to acquire private key, error: %lu", dwErr);
return WS_CRYPTO_FAILED;
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

SignWithCertStoreKey() claims to support a legacy CryptoAPI signing path (dwKeySpec != CERT_NCRYPT_KEY_SPEC), but CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG prevents acquiring CAPI provider handles. That makes the legacy branch effectively unreachable and will also fail for certificates backed by legacy providers. If legacy support is intended, acquire without CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG (or attempt NCRYPT first and fall back to non-NCRYPT flags on failure).

Suggested change
/* Get the private key handle from the certificate */
if (!CryptAcquireCertificatePrivateKey(pCertContext,
CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG | CRYPT_ACQUIRE_SILENT_FLAG,
NULL, &hCryptProv, &dwKeySpec, &fCallerFreeProv)) {
DWORD dwErr = GetLastError();
WLOG(WS_LOG_DEBUG, "SignWithCertStoreKey: Failed to acquire private key, error: %lu", dwErr);
return WS_CRYPTO_FAILED;
/* Get the private key handle from the certificate.
* Prefer CNG/NCRYPT first, but fall back to legacy CryptoAPI providers
* so the non-NCRYPT signing path below remains reachable.
*/
if (!CryptAcquireCertificatePrivateKey(pCertContext,
CRYPT_ACQUIRE_ONLY_NCRYPT_KEY_FLAG | CRYPT_ACQUIRE_SILENT_FLAG,
NULL, &hCryptProv, &dwKeySpec, &fCallerFreeProv)) {
if (!CryptAcquireCertificatePrivateKey(pCertContext,
CRYPT_ACQUIRE_SILENT_FLAG,
NULL, &hCryptProv, &dwKeySpec, &fCallerFreeProv)) {
DWORD dwErr = GetLastError();
WLOG(WS_LOG_DEBUG, "SignWithCertStoreKey: Failed to acquire private key, error: %lu", dwErr);
return WS_CRYPTO_FAILED;
}

Copilot uses AI. Check for mistakes.
Comment on lines +15370 to +15386
word32 halfSz = sigSz / 2;
word32 rOff = 0, sOff = 0;
r = rs;
s = rs + halfSz;
WMEMCPY(r, sig, halfSz);
WMEMCPY(s, sig + halfSz, halfSz);
/* Trim leading zeroes */
while (rOff < halfSz - 1 && r[rOff] == 0) rOff++;
while (sOff < halfSz - 1 && s[sOff] == 0) sOff++;
if (rOff > 0) {
WMEMMOVE(r, r + rOff, halfSz - rOff);
}
rSz = halfSz - rOff;
if (sOff > 0) {
WMEMMOVE(s, s + sOff, halfSz - sOff);
}
sSz = halfSz - sOff;
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This ECDSA cert-store path assumes sigSz is even and equal to 2 * keySizeBytes, but it does not validate that sigSz is non-zero, even, and within the bounds of the rs buffer before splitting and copying. If NCryptSignHash returns an unexpected size, this can cause buffer overreads/writes. Add explicit checks (e.g., sigSz % 2 == 0, halfSz <= sizeof(rs)/2) before the WMEMCPYs and return an error otherwise.

Suggested change
word32 halfSz = sigSz / 2;
word32 rOff = 0, sOff = 0;
r = rs;
s = rs + halfSz;
WMEMCPY(r, sig, halfSz);
WMEMCPY(s, sig + halfSz, halfSz);
/* Trim leading zeroes */
while (rOff < halfSz - 1 && r[rOff] == 0) rOff++;
while (sOff < halfSz - 1 && s[sOff] == 0) sOff++;
if (rOff > 0) {
WMEMMOVE(r, r + rOff, halfSz - rOff);
}
rSz = halfSz - rOff;
if (sOff > 0) {
WMEMMOVE(s, s + sOff, halfSz - sOff);
}
sSz = halfSz - sOff;
word32 halfSz;
word32 rOff = 0, sOff = 0;
if (sigSz == 0 || (sigSz & 1) != 0) {
WLOG(WS_LOG_DEBUG,
"SUAR: Invalid cert store ECC signature size");
ret = WS_ECC_E;
}
else {
halfSz = sigSz / 2;
if (halfSz > (word32)(sizeof(rs) / 2)) {
WLOG(WS_LOG_DEBUG,
"SUAR: Cert store ECC signature too large");
ret = WS_ECC_E;
}
else {
r = rs;
s = rs + halfSz;
WMEMCPY(r, sig, halfSz);
WMEMCPY(s, sig + halfSz, halfSz);
/* Trim leading zeroes */
while (rOff < halfSz - 1 && r[rOff] == 0) rOff++;
while (sOff < halfSz - 1 && s[sOff] == 0) sOff++;
if (rOff > 0) {
WMEMMOVE(r, r + rOff, halfSz - rOff);
}
rSz = halfSz - rOff;
if (sOff > 0) {
WMEMMOVE(s, s + sOff, halfSz - sOff);
}
sSz = halfSz - sOff;
}
}

Copilot uses AI. Check for mistakes.
Comment on lines +388 to +405

wStoreName = (wchar_t*)WMALLOC(storeNameLen * sizeof(wchar_t), heap, DYNTYPE_SSHD);
wSubjectName = (wchar_t*)WMALLOC(subjectNameLen * sizeof(wchar_t), heap, DYNTYPE_SSHD);

if (wStoreName == NULL || wSubjectName == NULL) {
wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Memory allocation failed for cert store strings");
ret = WS_MEMORY_E;
} else {
MultiByteToWideChar(CP_UTF8, 0, hostKeyStore, -1, wStoreName, storeNameLen);
MultiByteToWideChar(CP_UTF8, 0, hostKeyStoreSubject, -1, wSubjectName, subjectNameLen);

ret = wolfSSH_CTX_UsePrivateKey_fromStore(*ctx, wStoreName, dwFlags, wSubjectName);
if (ret != WS_SUCCESS) {
wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Failed to load host key from certificate store");
}

WFREE(wStoreName, heap, DYNTYPE_SSHD);
WFREE(wSubjectName, heap, DYNTYPE_SSHD);
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

MultiByteToWideChar() can return 0 on failure. In that case, storeNameLen/subjectNameLen will be 0 and the subsequent WMALLOC(0) + MultiByteToWideChar(..., cchWideChar=0) calls are invalid and may crash or silently misbehave. Mirror the error handling used in wolfSSH_ParseCertStoreSpec() (check lengths for 0 and fail early with a log + error code).

Suggested change
wStoreName = (wchar_t*)WMALLOC(storeNameLen * sizeof(wchar_t), heap, DYNTYPE_SSHD);
wSubjectName = (wchar_t*)WMALLOC(subjectNameLen * sizeof(wchar_t), heap, DYNTYPE_SSHD);
if (wStoreName == NULL || wSubjectName == NULL) {
wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Memory allocation failed for cert store strings");
ret = WS_MEMORY_E;
} else {
MultiByteToWideChar(CP_UTF8, 0, hostKeyStore, -1, wStoreName, storeNameLen);
MultiByteToWideChar(CP_UTF8, 0, hostKeyStoreSubject, -1, wSubjectName, subjectNameLen);
ret = wolfSSH_CTX_UsePrivateKey_fromStore(*ctx, wStoreName, dwFlags, wSubjectName);
if (ret != WS_SUCCESS) {
wolfSSH_Log(WS_LOG_ERROR, "[SSHD] Failed to load host key from certificate store");
}
WFREE(wStoreName, heap, DYNTYPE_SSHD);
WFREE(wSubjectName, heap, DYNTYPE_SSHD);
if (storeNameLen == 0 || subjectNameLen == 0) {
wolfSSH_Log(WS_LOG_ERROR,
"[SSHD] Failed to convert cert store strings to wide characters");
ret = WS_BAD_ARGUMENT;
}
else {
wStoreName = (wchar_t*)WMALLOC(storeNameLen * sizeof(wchar_t),
heap, DYNTYPE_SSHD);
wSubjectName = (wchar_t*)WMALLOC(subjectNameLen * sizeof(wchar_t),
heap, DYNTYPE_SSHD);
if (wStoreName == NULL || wSubjectName == NULL) {
wolfSSH_Log(WS_LOG_ERROR,
"[SSHD] Memory allocation failed for cert store strings");
ret = WS_MEMORY_E;
} else {
MultiByteToWideChar(CP_UTF8, 0, hostKeyStore, -1,
wStoreName, storeNameLen);
MultiByteToWideChar(CP_UTF8, 0, hostKeyStoreSubject, -1,
wSubjectName, subjectNameLen);
ret = wolfSSH_CTX_UsePrivateKey_fromStore(*ctx, wStoreName,
dwFlags, wSubjectName);
if (ret != WS_SUCCESS) {
wolfSSH_Log(WS_LOG_ERROR,
"[SSHD] Failed to load host key from certificate store");
}
WFREE(wStoreName, heap, DYNTYPE_SSHD);
WFREE(wSubjectName, heap, DYNTYPE_SSHD);
}

Copilot uses AI. Check for mistakes.
AS_IF([test "x$ENABLED_WINDOWS_CERT_STORE" = "xyes"],
[AS_IF([test "x$ENABLED_CERTS" != "xyes"],
[AC_MSG_ERROR([--enable-windows-cert-store requires X.509 cert support (--enable-certs)])])
AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFSSH_WINDOWS_CERT_STORE"])
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The configure-time flag enables compilation (-DWOLFSSH_WINDOWS_CERT_STORE) but does not add the required Windows crypto libraries to the link step for autotools builds (e.g., -lcrypt32 -lncrypt). This can lead to link failures in MinGW/MSYS2 environments. Consider extending configure logic to conditionally append these libs to LIBS when building on Windows targets and the feature is enabled.

Suggested change
AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFSSH_WINDOWS_CERT_STORE"])
AM_CPPFLAGS="$AM_CPPFLAGS -DWOLFSSH_WINDOWS_CERT_STORE"
AS_CASE([$host],
[*mingw*|*msys*|*cygwin*],[LIBS="$LIBS -lcrypt32 -lncrypt"])])

Copilot uses AI. Check for mistakes.


static void RefreshPublicKeyAlgo(WOLFSSH_CTX* ctx)
void RefreshPublicKeyAlgo(WOLFSSH_CTX* ctx)
Copy link

Copilot AI Apr 14, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

RefreshPublicKeyAlgo was changed from static to a global definition, but the declaration in wolfssh/internal.h uses WOLFSSH_LOCAL which may enforce internal linkage/hidden visibility depending on platform. To avoid linkage/visibility mismatches, align the definition with the header (e.g., use WOLFSSH_LOCAL void RefreshPublicKeyAlgo(...) in the definition if that’s the intended visibility), or adjust the header to match the new exported/non-static intent.

Suggested change
void RefreshPublicKeyAlgo(WOLFSSH_CTX* ctx)
WOLFSSH_LOCAL void RefreshPublicKeyAlgo(WOLFSSH_CTX* ctx)

Copilot uses AI. Check for mistakes.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants